package com.morpheus.redis.lettuce.config;

import java.time.Duration;
import java.util.Optional;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.morpheus.redis.lettuce.annotation.ConditionalOnLettuceStandaloneConfig;

@Configuration
@EnableConfigurationProperties({ RedisProperties.class, LettuceProperties.class })
@ConditionalOnLettuceStandaloneConfig
@Order(1)
public class LettuceStandaloneAutoConfiguration {
	private static final Logger LOGGER = LoggerFactory.getLogger(LettuceStandaloneAutoConfiguration.class);
	private static final String DEFAULT_JMX_NAME_BASE = "lettuce";
	private static final String DEFAULT_JMX_NAME_PREFIX = "jmx";
	@Value("${spring.application.name}")
	private String appName = null;

	@SuppressWarnings("all")
	@Bean
	public GenericObjectPoolConfig poolConfig(RedisProperties redisProperties, LettuceProperties lettuceProperties) {
		LOGGER.debug("redisProperties={}, lettuceProperties={}", redisProperties, lettuceProperties);
		GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
		poolConfig.setJmxEnabled(lettuceProperties.getJmx().isEnabled());
		poolConfig.setJmxNameBase(
				Optional.ofNullable(lettuceProperties.getJmx().getName().getBase()).orElse(DEFAULT_JMX_NAME_BASE));
		poolConfig.setJmxNamePrefix(
				Optional.ofNullable(lettuceProperties.getJmx().getName().getPrefix()).orElse(DEFAULT_JMX_NAME_PREFIX));
		poolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
		poolConfig.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle());
		poolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());
		poolConfig.setMaxWaitMillis(redisProperties.getLettuce().getPool().getMaxWait().toMillis());
		poolConfig.setTimeBetweenEvictionRunsMillis(
				redisProperties.getLettuce().getPool().getTimeBetweenEvictionRuns().toMillis());
		poolConfig.setTestOnBorrow(lettuceProperties.getTest().isOnBorrow());
		poolConfig.setTestOnCreate(lettuceProperties.getTest().isOnCreate());
		poolConfig.setTestOnReturn(lettuceProperties.getTest().isOnReturn());
		poolConfig.setTestWhileIdle(lettuceProperties.getTest().isWhileIdle());
		return poolConfig;
	}

	@SuppressWarnings("all")
	private LettuceClientConfiguration getLettuceClientConfiguration(GenericObjectPoolConfig poolConfig,
			RedisProperties redisProperties) {
		Duration timeout = redisProperties.getTimeout();
		LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration
				.builder().commandTimeout(timeout);
		builder.poolConfig(poolConfig);
		return builder.build();
	}

	@Bean
	public RedisStandaloneConfiguration redisStandaloneConfiguration(RedisProperties redisProperties) {
		RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(
				redisProperties.getHost(), redisProperties.getPort());
		redisStandaloneConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
		return redisStandaloneConfiguration;
	}

	@SuppressWarnings("all")
	@Bean(destroyMethod = "destroy")
	public RedisConnectionFactory connectionFactory(RedisStandaloneConfiguration redisStandaloneConfiguration,
			GenericObjectPoolConfig poolConfig, RedisProperties redisProperties) {
		LettuceClientConfiguration clientConfiguration = this.getLettuceClientConfiguration(poolConfig, redisProperties);
		LettuceConnectionFactory redisConnectionFactory = new LettuceConnectionFactory(redisStandaloneConfiguration, clientConfiguration);
		return redisConnectionFactory;
	}

	@Bean
	@ConditionalOnBean({ LettuceStandaloneAutoConfiguration.class })
	public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {
		StringRedisTemplate stringRedisTemplate = new StringRedisTemplate(connectionFactory);
		return stringRedisTemplate;
	}

	@Bean(name = "redisTemplate")
	@ConditionalOnBean({ LettuceStandaloneAutoConfiguration.class })
	// @ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
		redisTemplate.setConnectionFactory(connectionFactory);
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		return redisTemplate;
	}
}
